package 多线程.AQS.CyclicBarriereTest;

import lombok.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CyclicBarrier;

/**
 * CyclicBarrier是一个同步屏障，它允许多个线程相互等待，直到到达某个公共屏障点，才能继续执行。
 * 通常用来实现多个线程在同一个屏障处等待，然后再一起继续执行的操作。
 *
 * CyclicBarrier也维护了一个类似计数器的变量，通过CyclicBarrier的构造函数指定，
 * 需要大于0，否则抛IllegalArgumenException异常。当线程到达屏障位置时，调用await()方法进行阻塞，
 * 直到所有线程到达屏障位置时，所有线程才会被释放，而屏障将会被重置为初始值以便下次使用。
 * 不同线程调用await可使得屏障次数减1，相同线程调用多次只会起到一次的效果
 *
 * CyclicBarrier(int parties)：CyclicBarrier的构造方法，可通过parties参数指定需要到达屏障的线程个数，但是要大于0，否则会抛IllegalArgumentException异常。
 * CyclicBarrier(int parties,Runnable barrierAction)：另一个构造方法，parties作用同上，barrierAction表示最后一个到达屏障点的线程要执行的逻辑。
 * int await()：表示线程到达屏障点，并等待其它线程到达，返回值表示当前线程在屏障中的位置（第几个到达的）。
 * int await(long timeout,TimeUnit unit)：与await()类似，但是设置了超时时间，如果超过指定的时间后，仍然还有线程没有到达屏障点，则等待的线程会被唤醒并执行后续操作。
 * void reset()：重置屏障状态，即将屏障计数器重置为初始值。
 * int getParties()：获取需要同步的线程数量。
 * int getNumberWaiting()：获取当前正在等待的线程数量。
 *
 * 实例：
 *      多线程去查询结果集，加快查询速率，然后组合到同一个list返回
 */
@AllArgsConstructor
@NoArgsConstructor
public class CyclicBarrierDemo {
    private CyclicBarrier cyclicBarrier;
    //存储每个工作线程的结果
    private List<Integer> results = Collections.synchronizedList(new ArrayList<>());
    private Random random = new Random();
    //每个工作线程要产生的结果数量
    private int resultNums;
    //要执行的线程数量
    private int workerNums;

    //每个线程的执行逻辑
    class AcquireNums implements Runnable {
        @Override
        public void run() {
            String thisThreadName = Thread.currentThread().getName();

            //Random产生随机数并存入
            for (int i = 0; i < resultNums; i++) {
                Integer num = random.nextInt(10);
                System.out.println(thisThreadName + ": 随机结果 - " + num);
                results.add(num);
            }

            try {
                System.out.println(thisThreadName + " 等待其它线程到达屏障点.");
                //阻塞其它线程
                cyclicBarrier.await();
            } catch (Exception e) {

            }
        }
    }

    //所有线程到达屏障点时执行的操作
    class AfterThreadWork implements Runnable {
        @Override
        public void run() {
            String thisThreadName = Thread.currentThread().getName();
            int sum = 0;
            //计算结果
            for (Integer result : results) {
                System.out.println("加"+result);
                sum += result;
            }
            System.out.println(thisThreadName + ": 总和 = " + sum);
        }
    }

    //程序入口
    public void runSimulation(int numWorkers, int numberOfresults) {
        resultNums = numberOfresults;
        workerNums = numWorkers;
        //创建CyclicBarrier时指定需要到达屏障的线程数parties。
        //障碍跳闸时要执行的命令，如果没有操作,抛异常
        cyclicBarrier = new CyclicBarrier(workerNums, new AfterThreadWork());
        System.out.println("创建" + workerNums + "个线程，每个线程获取" + resultNums + "个结果");
        for (int i = 0; i < workerNums; i++) {
            Thread worker = new Thread(new AcquireNums());
            worker.setName("Thread " + i);
            worker.start();
        }
    }

    public static void main(String[] args) {
        CyclicBarrierDemo demo = new CyclicBarrierDemo();
        demo.runSimulation(5, 3);
    }
}